package com.jinxin.platform.cdacsfacetalk.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.jinxin.platform.cdacsfacetalk.bean.CdcbiWxuserid;
import com.jinxin.platform.cdacsfacetalk.bean.DeviceInfo;
import com.jinxin.platform.cdacsfacetalk.common.Constant;
import com.jinxin.platform.cdacsfacetalk.common.HouseInfo;
import com.jinxin.platform.cdacsfacetalk.mapper.CdfacetalkWxuseridMapper;
import com.jinxin.platform.cdacsfacetalk.rpc.AcsFaceTalkRpc;
import com.jinxin.platform.cdacsfacetalk.service.IAcsFaceTalkService;
import com.jinxin.platform.cdacsfacetalk.utils.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * All rights Reserved, Designed By www.beonelot.com
 *
 * @version: V1.0
 * @Copyright: www.beonelot.com Inc. All rights reserved.
 * @Description：
 * @Date: 2021-05-14
 * @Author: Lingnan
 **/
@Service
@Slf4j
public class AcsFaceTalkServiceImpl implements IAcsFaceTalkService {
    @Resource
    private FaceTalkRedisUtil redisUtil;
    @Autowired
    private AcsFaceTalkRpc acsFaceTalkRpc;
    @Autowired
    private CdfacetalkWxuseridMapper facetalkWxuseridMapper;
    @Value("${cdacsfacetalk.wxopen.appid}")
    private String wxopenAppid; //公众号appid
    @Value("${cdacsfacetalk.wxopen.secret}")
    private String wxopenSecret; //公众号secret
    @Value("${cdacsfacetalk.miniparam.appid}")
    private String miniparamAppid; //小程序appid
    @Value("${cdacsfacetalk.miniparam.pagepath}")
    private String miniparampPagepath; //小程序跳转路径
    @Value("${cdacsfacetalk.sdk.appid}")
    private long sdkAppid;
    @Value("${cdacsfacetalk.sdk.secretkey}")
    private String sdkSecretkey;
    @Value("${cdacsfacetalk.url.get-accesstoken}")
    private String getAccesstokenUrl;
    @Value("${cdacsfacetalk.url.send-msg}")
    private String sendMsgUrl;
    @Value("${cdacsfacetalk.url.get-users}")
    private String getUsersUrl;
    @Value("${cdacsfacetalk.url.get-user-info}")
    private String getUsersInfoUrl;
    @Value("${cdacsfacetalk.msg.template-id}")
    private String templateId; // 模板消息
    private DateTimeFormatter df =
            DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    private ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 200, TimeUnit.MILLISECONDS,
            new ArrayBlockingQueue<>(5));

    /**
     *
     * @param controllersn 设备序列号(注意：设备的返回controllersn是大写的，而设备模块返回的是小写的，需要转化)
     * @param roomId
     * @return
     */
    @Override
    public Map<String, Object> faceAll(String controllersn, String roomId) {
        Map<String, Object> resultMap = new HashMap<>();
        if (StringUtils.isEmpty(controllersn) || StringUtils.isEmpty(roomId)) {
            resultMap.put("status", 500);
            resultMap.put("msg", "设备ID或房间ID不存在");
            return resultMap;
        }

        // ==============测试设备
//        long expiresTime = 604800; // 一天
//        String userId = Utils.getUuid();
//        String userSig = new TLSSigAPIv2(this.sdkAppid, this.sdkSecretkey).
//                genUserSig(userId, expiresTime);
//        if(!StringUtils.isEmpty(userSig)){
//            resultMap.put("status",200);
//            resultMap.put("msg","成功");
//            resultMap.put("roomId",Utils.getRandom());
//            resultMap.put("sign",userSig);
//            resultMap.put("userId",userId);
//            return resultMap;
//        }
        // ==============测试设备
        // 通过房间id查询业主的信息(需要openId，消息推送需要)
        // [{"houseId":"221321193","unitName":"1单元","idCard":"51138120161213017X","openId":"4dfc532c776a4f1b8a86dfeec201da62","authStatus":"yes","buildingId":"2008962588","buildingName":"1栋","phone":"13609036889","name":"测试使用","unitId":"946253297","communityName":"灵秀家园","id":"1709189404","faceInfo":"/upload/module/user/avatar/tmp_a9846510e71e837da14523932e838159.jpg","personnelStatus":"11","houseNum":"1203"}]
        List<HouseInfo> houseInfoList = acsFaceTalkRpc.getHouseInfoByRoomId(roomId);
        if(houseInfoList == null || houseInfoList.size() == 0){
            resultMap.put("status", 500);
            resultMap.put("msg", "获取房间信息失败");
            return resultMap;
        }
        String userSigDeviceKey = controllersn + "_" + Constant.REDIS_WEIXIN_USERSIG_DEVICE;
        long expiresTime = Constant.WEIXIN_USERSIG_EXPIRE_TIME; // 一天
        String userSig;//设备用userSig
        if (redisUtil.hasKey(userSigDeviceKey)) {
            userSig = redisUtil.get(userSigDeviceKey);
        } else {
            userSig = new TLSSigAPIv2(this.sdkAppid, this.sdkSecretkey).
                    genUserSig(controllersn, expiresTime);
            if (!StringUtils.isEmpty(userSig)) {
                redisUtil.setForTimeCustom(userSigDeviceKey, userSig, (expiresTime - 10), TimeUnit.SECONDS);
            }
        }
        // 公众号推送消息
        int randomNum = Utils.getRandom();
        String roodId = randomNum + "";
        // 判断设备类型，只有单元机推送消息才有单元(小区+楼栋+单元),小区门禁显示:小区+门禁位置
        String serialNum = controllersn.toLowerCase();
        DeviceInfo device = acsFaceTalkRpc.getDeviceTypeByMac(serialNum);
        if(device == null){
            resultMap.put("status", 500);
            resultMap.put("msg", "查询设备失败");
            return resultMap;
        }
        boolean isDyj = "单元门禁".equals(device.getType());
        houseInfoList.forEach(houseInfo->{
            String unionId = houseInfo.getUnionId();
            String communityName = houseInfo.getCommunityName();
            String fullRoomInfo= isDyj ? (communityName + houseInfo.getBuildingName()) : (communityName + device.getLocation());
            executor.execute(new SendMsgTask(this.miniparamAppid, this.sdkAppid, this.sdkSecretkey, roodId, unionId, fullRoomInfo,serialNum,device.getProductCode()));
        });
        if(!StringUtils.isEmpty(userSig)){
            resultMap.put("status", 200);
            resultMap.put("msg", "成功");
            resultMap.put("roomId", randomNum);
            resultMap.put("sign", userSig);
        }else {
            resultMap.put("status", 500);
            resultMap.put("msg", "失败");
            resultMap.put("roomId", randomNum);
            resultMap.put("sign", "");
        }
        return resultMap;
    }

    @Override
    public void validateToken(HttpServletRequest request, HttpServletResponse response) {
        String token = Constant.WEIXIN_TOKEN;
        String signature = request.getParameter("signature");
        String timestamp = request.getParameter("timestamp");
        String nonce = request.getParameter("nonce");
        String echostr = request.getParameter("echostr");

        log.info("【signature】=" + signature);
        log.info("【timestamp】=" + timestamp);
        log.info("【nonce】=" + nonce);
        log.info("【echostr】=" + echostr);
        log.info("【token】=" + token);

        String[] arrTmp = {token, timestamp, nonce};
        Arrays.sort(arrTmp);
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < arrTmp.length; i++) {
            sb.append(arrTmp[i]);
        }
        String pwd = StringUtil.Encrypt(sb.toString());

        log.info("【pwd】=" + pwd);

        if (pwd.equals(signature)) {
            if (!"".equals(echostr) && echostr != null) {
                try {
                    log.info("*******************消息验证成功*******************");
                    response.getWriter().print(echostr);
                } catch (IOException e) {
                    log.error("写回数据异常：" + e.getMessage());
                }
            }
        } else {
            log.info("*******************消息验证失败*******************");
        }
    }

    class SendMsgTask implements Runnable{
        private String miniparamAppid;//小程序appid
        private long sdkAppid;
        private String sdkSecretkey;
        private String roomId;//房间号
        private String unionId;
        private String houseInfo;
        private String serialNum; // 设备开门需要，是小写的
        private String productCode;

        public SendMsgTask(String miniparamAppid, long sdkAppid,String sdkSecretkey,String roomId, String unionId, String houseInfo,String serialNum,String productCode) {
            this.miniparamAppid = miniparamAppid;
            this.sdkAppid = sdkAppid;
            this.sdkSecretkey = sdkSecretkey;
            this.roomId = roomId;
            this.unionId = unionId;
            this.houseInfo = houseInfo;
            this.serialNum = serialNum;
            this.productCode = productCode;
        }

        @Override
        public void run() {
            String accessToken = getAccessToken();
            if(!StringUtils.isEmpty(accessToken)){
                String url = sendMsgUrl + "?access_token=" + accessToken;
                Map<String,Object> paramMap = new HashMap<>();
                log.info("获取的unionId："+ this.unionId);
                //由于公众号获取的openId与小程序的openId不一致，无法直接通过openId发送模板消息
                // 由于用户的unionId都是不变的，可以通过查询获取所有关注了该公众号的人员的openId,
                // 再通过获取用户基本信息(UnionID机制)查询出unionid与传过来的unionId匹配，获取公众号的openId
                String openId = null;//公众号的openId
                // 先通过数据库表CdcbiWxuserid查询openId，如果不存在则采用上述方法
                CdcbiWxuserid wxuserid = facetalkWxuseridMapper.queryById(this.unionId);
                if(wxuserid != null){
                    openId = wxuserid.getGzhOpenId();
                }
                if(StringUtils.isEmpty(openId)){
                    try {
                        Map<String,String> paramMap1 = new HashMap<>();
                        paramMap1.put("access_token",accessToken);
                        HttpClientResult result =
                                HttpClientPoolUtil.doGet(getUsersUrl, null, paramMap1, true);
                        String content = result.getContent();
                        log.info("获取公众号用户列表：" + content);
                        if(Constant.REQUEST_STATUS_SUCCEED.equals(result.getCode())){
                            JSONObject resultJson = JSON.parseObject(content);
                            if(resultJson != null){
                                int total = resultJson.getInteger("total");
                                if(total > 0){
                                    JSONObject dataJson = resultJson.getJSONObject("data");
                                    if(dataJson != null){
                                        String[] openids = dataJson.getObject("openid", String[].class);
                                        for (String openid : openids) {
                                            paramMap1.put("openid",openid);
                                            HttpClientResult result1 =
                                                    HttpClientPoolUtil.doGet(getUsersInfoUrl, null, paramMap1, true);
                                            String content1 = result1.getContent();
                                            if(Constant.REQUEST_STATUS_SUCCEED.equals(result1.getCode())){
                                                JSONObject infoJson = JSON.parseObject(content1);
                                                if(infoJson != null && infoJson.containsKey("unionid")){
                                                    String unionid = infoJson.getString("unionid").trim();
                                                    if(this.unionId.equals(unionid)){
                                                        log.info("找到该用户unionId对应的openId：" + openid);
                                                        openId = openid;
                                                        break;
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                paramMap.put("touser",openId);
                paramMap.put("template_id",templateId);
                paramMap.put("url","");
                paramMap.put("topcolor","#FF0000");
                JSONObject miniParamJson = new JSONObject();
                miniParamJson.put("appid", this.miniparamAppid);
                String userSig;//小程序用userSig
                String userSigUserKey = openId + "_" +Constant.REDIS_WEIXIN_USERSIG_USER;
                if(redisUtil.hasKey(userSigUserKey)){
                    userSig = redisUtil.get(userSigUserKey);
                }else {
                    long expiresTime = Constant.WEIXIN_USERSIG_EXPIRE_TIME-10;
                    userSig = new TLSSigAPIv2(this.sdkAppid, this.sdkSecretkey).
                            genUserSig(openId, expiresTime);
                    if(!StringUtils.isEmpty(userSig)){
                        redisUtil.setForTimeCustom(userSigUserKey, userSig, (expiresTime - 10), TimeUnit.SECONDS);
                    }
                }
                if(!StringUtils.isEmpty(userSig)){
                    String paramsPath = "?sdkAppID="+this.sdkAppid +"&userID="+openId+"&userSig="+userSig+
                            "&roomId="+this.roomId+"&serialNum="+this.serialNum+"&productCode="+this.productCode+
                            "&location="+this.houseInfo;
                    String pagepath = AcsFaceTalkServiceImpl.this.miniparampPagepath+paramsPath;
                    log.info("小程序跳转路径：" + pagepath);
                    miniParamJson.put("pagepath",pagepath);
                    paramMap.put("miniprogram",miniParamJson);
                    JSONObject dataJson = new JSONObject();
                    JSONObject titleJson = new JSONObject();
                    titleJson.put("value","您好，您有新的呼叫提醒");
                    titleJson.put("color","#173177");
                    dataJson.put("first",titleJson);
                    JSONObject locationJson = new JSONObject();
                    locationJson.put("value",houseInfo);
                    locationJson.put("color","#173177");
                    dataJson.put("keyword1",locationJson);
                    JSONObject timeJson = new JSONObject();
                    timeJson.put("value", df.format(LocalDateTime.now()));
                    timeJson.put("color","#173177");
                    dataJson.put("keyword2",timeJson);
                    JSONObject remarkJson = new JSONObject();
                    remarkJson.put("value","请点击详情进行查看和开门。");
                    remarkJson.put("color","#173177");
                    dataJson.put("remark",remarkJson);
                    paramMap.put("data",dataJson);
                    String postJson = JSON.toJSONString(paramMap);
                    try {
                        HttpClientResult result =
                                HttpClientPoolUtil.doPost(url, null, postJson, true);
                        String content = result.getContent();
                        log.info("发送模板消息请求返回：" + content);
                        if(Constant.REQUEST_STATUS_SUCCEED.equals(result.getCode())){
                            JSONObject resultJson = JSON.parseObject(content);
                            if(resultJson != null && resultJson.containsKey("errcode")){
                                int errcode = resultJson.getInteger("errcode");
                                if(errcode == 0){
                                    log.info("发送模板消息成功");
                                    //再发送19次消息
                                    String key = Constant.FACE_TALK_CONNECTED_PREFIX + this.roomId;
                                    //每次去请求redis。看是否已经接通，接通了就不推送了
                                    for (int i = 0; i < 19; i++) {
                                        if(redisUtil.hasKey(key) && !StringUtils.isEmpty(redisUtil.get(key))){
                                            redisUtil.delete(key);
                                            break;
                                        }
                                        TimeUnit.SECONDS.sleep(1);
                                        HttpClientPoolUtil.doPost(url, null, postJson, true);
                                    }
                                }
                            }
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
        }
    }

    private String getAccessToken() {
        String key = Constant.REDIS_WEIXIN_TOKEN_KEY;
        if(redisUtil.hasKey(key)){
            return redisUtil.get(key);
        }
        Map<String, String> params = new HashMap<>();
        params.put("grant_type","client_credential");
        params.put("appid",wxopenAppid);
        params.put("secret",wxopenSecret);
        try {
            HttpClientResult result = HttpClientPoolUtil.doGet(getAccesstokenUrl, params, true);
            String content = result.getContent();
            log.info("获取AccessToken请求返回：" + content);
            if(Constant.REQUEST_STATUS_SUCCEED.equals(result.getCode())){
                JSONObject tokenJson = JSON.parseObject(content);
                if(tokenJson != null){
                    String accessToken = tokenJson.getString("access_token");
                    long expiresTime = tokenJson.getLong("expires_in");
                    redisUtil.setForTimeCustom(key,accessToken,(expiresTime-30),TimeUnit.SECONDS);
                    return accessToken;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

}
}
